
#include "testCryptOChain.h"
#include "../crypt/DSAKeyPairGenerator.h"
#include "../crypt/RSAKeyPairGenerator.h"

void tests::testCryptOChain(apr_pool_t *mp)
{
   testCryptOChainPipe(10, 1000, 10000, mp);
   testCryptOChainBlock(10, 1000, 10000, mp);
}

void tests::testCryptOChainPipe(int iterations, int max_block, int max_length, apr_pool_t *mp)
{
   for (int i = 0; i < iterations; i++)
   {
      DSAKeyPairGenerator dsakpgen(DSA_BITS, "DSAkey");
      DSAPublicKey *d_pubkey = dsakpgen.popPublicKey();
      DSAPrivateKey *d_prvkey = dsakpgen.popPrivateKey();
      RSAKeyPairGenerator rsakpgen(RSA_BITS, "RSAkey");
      RSAPublicKey *r_pubkey = rsakpgen.popPublicKey();
      RSAPrivateKey *r_prvkey = rsakpgen.popPrivateKey();

      APRBucketBrigade t_input(mp);
      APRBucketBrigade t_intvl(mp);
      APRBucketBrigade t_output(mp);

      CryptOChain *forward = new CryptOChain(t_input, t_intvl, mp);
      CryptOChain *backward = new CryptOChain(t_intvl, t_output, mp);
      
      EncryptionKey *enc = (EncryptionKey *)r_pubkey;
      DecryptionKey *dec = (DecryptionKey *)r_prvkey;
      SigningKey *sgn = (SigningKey *)d_prvkey;
      CheckingKey *chk = (CheckingKey *)d_pubkey;
      CheckingKey *wrong_chk = (CheckingKey *)r_pubkey;

      forward->buildEncryptionChain(enc, sgn, 1);
      int testtodo = rand() % 2;
      if (testtodo)
         backward->buildDecryptionChain(dec, chk, 1);
      else
         backward->buildDecryptionChain(dec, wrong_chk, 1);

      // Random length much more than a single key
      int randlen = rand() % max_length;
      // Create a block of (pseudo-)random data
      char *pseudorandom = (char *)malloc(sizeof(char)*randlen);
      for (int j = 0; j < randlen; j++)
         pseudorandom[j] = (rand() & 0xFF0) >> 8;

      forward->processPipeBegin();
      backward->processPipeBegin();
      
      int complete = 0;
      int blocksize = 16384;
      int nidx = 0;
      while (nidx < randlen)
      {
         int randblocklen = rand() % max_block;
         randblocklen = ((randblocklen + nidx) > randlen) ? (randlen - nidx) : randblocklen;
         int randblocklen_added = t_input.writeToStream(&(pseudorandom[nidx]), randblocklen);
         nidx += randblocklen_added;
         forward->processPipeStep(blocksize, blocksize);
         backward->processPipeStep(blocksize, blocksize);
         t_input.removeFromStream(randblocklen_added);
      }
      while (!complete)
      {
         int rcomplete = 0;
         rcomplete |= forward->processPipeStep(blocksize, blocksize);
         rcomplete |= backward->processPipeStep(blocksize, blocksize);
         complete = (!rcomplete);
      }
      
      forward->processPipeEnd();
      backward->processPipeEnd();

      delete d_pubkey; delete d_prvkey;
      delete r_pubkey; delete r_prvkey;
      
      size_t new_plain_size = 0;
      char *new_plain_data = NULL;
      t_output.bucketBrigadeToBuffer(&new_plain_data, &new_plain_size);
      if (new_plain_size != randlen)
      {
         errprint("error: original data size was %" APR_SIZE_T_FMT ", new data size is %" APR_SIZE_T_FMT, (apr_size_t)randlen, new_plain_size);
         //printf("old data:\n");
         //StringUtils::printBinary(pseudorandom, randlen);
         //printf("new data:\n");
         //StringUtils::printBinary(new_plain_data, randlen);
      } else if (memcmp(pseudorandom, new_plain_data, sizeof(char)*randlen) != 0)
      {
         errprint("error: original data does not match new data");
      } else
      {
         printf("CryptOChain Pipe(issame) test %d passed - data length %d\n", i, randlen);
         printf("CryptOChain forward:%d backward:%d(%d) %s\n", forward->processWasSecure(), backward->processWasSecure(), testtodo, (testtodo == backward->processWasSecure()) ? "passed" : "failed");
      }
      delete forward;
      delete backward;

      free(pseudorandom);
      free(new_plain_data);
   }
}

#include "../utils/StringUtils.h"

void tests::testCryptOChainBlock(int iterations, int max_block, int max_length, apr_pool_t *mp)
{
   for (int i = 0; i < iterations; i++)
   {
      DSAKeyPairGenerator dsakpgen(DSA_BITS, "DSAkey");
      DSAPublicKey *d_pubkey = dsakpgen.popPublicKey();
      DSAPrivateKey *d_prvkey = dsakpgen.popPrivateKey();
      RSAKeyPairGenerator rsakpgen(RSA_BITS, "RSAkey");
      RSAPublicKey *r_pubkey = rsakpgen.popPublicKey();
      RSAPrivateKey *r_prvkey = rsakpgen.popPrivateKey();
      
      // Random length much more than a single key
      int randlen = rand() % max_length;
      // Create a block of (pseudo-)random data
      char *pseudorandom = (char *)malloc(sizeof(char)*randlen);
      for (int j = 0; j < randlen; j++)
         pseudorandom[j] = (rand() & 0xFF0) >> 8;
   
      CryptOChain *encwrap = new CryptOChain(mp);
      encwrap->buildEncryptionChain((EncryptionKey *)r_pubkey, (SigningKey *)d_prvkey, 1);
      int testtodo = rand() % 2;
      CryptOChain *decwrap = new CryptOChain(mp);
      decwrap->buildDecryptionChain((DecryptionKey *)r_prvkey, (testtodo) ? ((CheckingKey *)d_pubkey) : ((CheckingKey *)r_pubkey), 1);

      apr_size_t t_interm_size = 0;
      char *t_interm = encwrap->processDataBlock(pseudorandom, randlen, &t_interm_size);
      
      apr_size_t t_output_size = 0;
      char *t_output = decwrap->processDataBlock(t_interm, t_interm_size, &t_output_size);
      
      apr_size_t new_plain_size = t_output_size;
      char *new_plain_data = (char *)malloc(sizeof(char)*new_plain_size);
      memcpy(new_plain_data, t_output, new_plain_size);

      delete d_pubkey; delete d_prvkey;
      delete r_pubkey; delete r_prvkey;
      
      if (new_plain_size != randlen)
      {
         errprint("error: original data size was %" APR_SIZE_T_FMT ", new data size is %" APR_SIZE_T_FMT, (apr_size_t)randlen, new_plain_size);
         printf("old data:\n");
         StringUtils::printBinary(pseudorandom, randlen);
         printf("new data:\n");
         StringUtils::printBinary(new_plain_data, randlen);
      } else if (memcmp(pseudorandom, new_plain_data, sizeof(char)*randlen) != 0)
      {
         errprint("error: original data does not match new data");
      } else
      {
         printf("CryptOChain Block(issame) test %d passed - data length %d\n", i, randlen);
         printf("CryptOChain forward:%d backward:%d(%d) %s\n", encwrap->processWasSecure(), decwrap->processWasSecure(), testtodo, (testtodo == decwrap->processWasSecure()) ? "passed" : "failed");
      }
      free(pseudorandom);
      free(new_plain_data);
   }
}
